/*****************************************************
	Nome: 		VISED MED Module
	Versão:		3.7

	Funcionalidades:
	- Cálculo de forças
	- Integração Temporal

 *****************************************************/

#include "DEFS.H"
#include "../CORE.H"
#include "MODEL.H"
#include "MED.H"

#include "../fronteira/FronteiraRigida.h"
#include "../fronteira/FronteiraDestrutivel.h"
#include "../fronteira/FronteiraAbsortivaParcial.h"
#include "../fronteira/FronteiraAbsortiva.h"
#include "../forcas/molas/Molas.h"
#include "../forcas/molas/ScreeningMola.h"
#include "../forcas/Restricoes.h"
#include "../forcas/interacao/contato.h"
#include "../forcas/interacao/campo.h"
#include "../forcas/independentes/forcasIndependentes.h"
#include "../integracao/RungeKutta.h"
#include "../integracao/Euler.h"
#include "../integracao/Verlet.h"
#include "../colisao/Munjiza_nbs.h"
#include "../colisao/Munjiza_nbs_par.h"
#include "../colisao/Screening.h"
#include "../colisao/ScreeningParalelo.h"
#include "../colisao/Munjiza.h"

#if (_MSC_VER >= 1700) || (__GNUC__ > 3)
#include "../colisao/ScreeningCollapse.h"
#endif

#ifdef RUNGEKUTTA_MINATO
char SPHERE::Step = 0;
#endif

void MED::Imprime(const char* TXT1,const char* TXT2)
{
    cout << AMARELO<< TXT1;
    for (int i=strlen(TXT1);i<56;i+=8) cout << '\t' ;
    cout << VERDE <<TXT2 << BRANCO<<"\n";
}
void MED::Imprime(const char* TXT1,const char* TXT2,const char* TXT3)
{
    cout << AMARELO<< TXT1 ;
    for (int i=strlen(TXT1);i<32;i+=8) cout << '\t' ;
    cout << VERMELHO <<TXT2;
    if(strlen(TXT2)<8)cout << '\t' ;
    cout << '\t' << VERDE<<TXT3 <<BRANCO<<"\n";
}

MED::MED()
{
    pCore = NULL;
    pModel = NULL;
    IsActive = false;
    min_rad = MAX_REAL;
	max_rad = 0.0;
    Critical_dT = MAX_REAL;
    qualOpcMola=4;
    tudoSeq=false;
    intSeq=false;
    detSeq=false;
    indepSeq=false;
    frontSeq=false;
    restrSeq=false;
    qualIntegracao=2;
    qualDetCol=1;
    #if (_MSC_VER >= 1700) || (linux)
    qualDetCampo=2;
    #else
    qualDetCampo=3;
    #endif
    numeroThreads=0;
}

/*
Conta quantas molas existem no modelo
e depois divide a lista de molas em pedaços
dependendo da quantidade de processadores
*/
int MED::OrganizaPonteirosMolas()
{
    int i=0;
    int offset;
    int tamanho=0;
    pModel->threadsS[0]=pModel->SPRList.Begin;
	for (SPRING* k = pModel->SPRList.Begin; k != NULL;k = k->Next)
	{
	    tamanho++;
	}
    offset=tamanho/pModel->numProceMola;
    for (SPRING* k = pModel->threadsS[0]; k != NULL; k = k->Next)
    {
		tamanho++;
		if (tamanho == offset)
		{
            i++;
            pModel->threadsS[i] = k;
		}
	}
	ImprimeMem("Molas",tamanho*sizeof(SPRING));
	return tamanho;
}
/*
Conta quantas esferas existem no modelo
e depois divide a lista de molas em pedaços
dependendo da quantidade de processadores
*/
void MED::OrganizaPonteirosEsferas(int tamanho)
{
    int i=0;
    //offset é a posição na lista
    //se o tamanho da lista for 10 e tivermos 2 processadores
    //um processador ficará com offset 0 e o outro 5
    int offset;
    offset=(tamanho)/(pModel->numProceEsf);
    tamanho=0;
    pModel->threads[0]=pModel->SPHList.Begin;
    for (SPHERE* s = pModel->threads[0]; s != NULL; s = s->Next) {
		tamanho++;
		if (tamanho == offset)
		{
            i++;
            pModel->threads[i] = s;
		}
	}
    ImprimeMem("Esferas",tamanho*sizeof(SPHERE));
}

/*
    Abaixo percorrendo toda a lista de esferas procurando o maior
    e menor elementos. Além disso tambem calcula o tamanho da lista
    e retorna
*/
int MED::CalculaMenorMaiorElementos()
{
    //tamanho é o tamanho da lista de esferas
    int tamanho=0;

	for (SPHERE* s = pModel->SPHList.Begin; s != NULL; s = s->Next) {
		if (s->Rad < min_rad) min_rad = s->Rad;
		if (s->Rad > max_rad) max_rad = s->Rad;
		tamanho++;
	}
	//max_rad*=2;
    return tamanho;
}

/*
Essa é a função que inicializa o MED
com os valores fornecidos da entrada na linha de comando
*/
void MED::Initialize( CORE* core )
{

	if (IsActive) abort();
	pCore = core;
	pModel = &(pCore->Model);
	IsActive = true;

    OrganizaPonteirosEsferas(CalculaMenorMaiorElementos());

    //Crio as estruturas para o cálculo das molas
    if (OrganizaPonteirosMolas()>0) EscolhaMolas();
    else molas=new ForcaMolas(); //Cria uma classe que não faz nada

    //Carregando os construtores para o cálculo dos passos
    if (pModel->rigidezManual)
    {
        contato = new Contato(pModel->kn,pModel->cn,pModel->gamma);
    }
    else contato = new Contato();

    campoForca = new CampoForca(pModel);

    //As funções abaixo instanciam os ponteiros
    //com as opções fornecidas na linha de comando
    //que ficam guardadas nas variáveis da classe MED
    EscolhaColisao();
    if (pModel->FF_Active) EscolhaCampo();
    colisaoLinhasEsferas = new Linhas (pModel,contato);
    EscolhaIntegracao();
    IniciaForcasIndependentes();
    IniciaFronteira();
    IniciaRestricoes();
}

void MED::Terminate() {

	if (!IsActive) abort();

	IsActive = false;
}

/*
Esta é a função mais importante, pois é quem adianta
o passo do cálculo.
cada um dos elementos Ex:. mola e fronteira
são ponteiros que representam o cálculo das molas
e o cálculo de colisão com a fronteira respectivamente
*/
void MED::CalculaForcas()
{
    fronteira->calcular();
    DetContatoEsferas->calcular();
    colisaoLinhasEsferas->calcular();
    if (pModel->FF_Active) calculoCampoForca->calcular();
}
void MED::CalculaForcasExternas()
{
    forcasIndependentes->calcular();
    molas->calcular();
    restricoes->calcular();
}

void MED::Integra()
{
    integracao->calcular();
}


void MED::FuncoesDeResolucao()
{
    //restricoes->imprimeForcasEstaticas();
    restricoes->imprimeForcasSockets();
}

/*
Guarda no ponteiro *molas qual o algoritmo a usar para o cálculo das Molas
*/
void MED::EscolhaMolas()
{
    if ((qualOpcMola==1)||tudoSeq)
    {
        Imprime("Cálculo de Molas:","Sequencial");
        molas = new ListaMolas (pModel);
    }
    else if (qualOpcMola==2)
    {
        Imprime("Cálculo de Molas","Semáforos","Paralelo");
        molas = new ListaMolasParalelo (pModel);
    }
    else
    {
        Imprime("Cálculo de Molas","Screening","Paralelo");
        molas = new ScreeningMola (pModel);
    }
}
/*
Guarda no ponteiro *integracao qual o algoritmo a usar para o etapa de integração
*/
void MED::EscolhaIntegracao()
{

    if (tudoSeq||intSeq)
    {
        if (qualIntegracao==1)
        {
            Imprime("Integração","Euler","Sequencial");
            integracao = new Euler (pModel);
        }
        else if (qualIntegracao==2)
        {
            Imprime("Integração","Verlet","Sequencial");
            integracao = new Verlet (pModel);
        }
        else
        {
            #ifdef RUNGEKUTTA_MINATO
            Imprime("Integração","Runge Kutta\t","Sequencial");
            integracao = new RungeKutta4ordem (pModel);
            #endif

            #ifndef RUNGEKUTTA_MINATO
            cout << VERMELHO << "Runge kutta desabilitado!\n";
            cout << "Se deseja habilitá-lo, defina RUNGEKUTTA_MINATO em RungeKutta.h e recompile o programa\n";
            cout << BRANCO;
            #endif
        }
    }
    else
    {
        if (qualIntegracao==1)
        {
            Imprime("Integração","Euler","Paralelo");
            integracao = new EulerParalelo (pModel);
        }
        else if (qualIntegracao==2)
        {
            Imprime("Integração","Verlet","Paralelo");
            integracao = new VerletParalelo (pModel);
        }
        else
        {
            #ifdef RUNGEKUTTA_MINATO
            Imprime("Integração","Runge Kutta\t","Paralelo");
            integracao = new RungeKutta4ordemParalelo (pModel);
            #endif

            #ifndef RUNGEKUTTA_MINATO
            cout << VERMELHO << "Runge kutta desabilitado!\n";
            cout << "Se deseja habilitá-lo, defina RUNGEKUTTA_MINATO em RungeKutta.h e recompile o programa\n";
            cout << BRANCO;
            #endif
        }

    }
}
/*
Guarda no ponteiro *DetContatoEsferas qual o algoritmo
a usar para o etapa de detecção de contato
*/
void MED::EscolhaColisao()
{
    if (tudoSeq||detSeq)
    {
        if (qualDetCol==1)
        {
            //cout << "Detecção (Contato):\t\t"<< VERMELHO <<"Screening\tSequencial\n";
            Imprime("Detecção (Contato)","Screening","Sequencial");
            DetContatoEsferas = new Screening  ( max_rad,pModel,contato);
        }
        else if (qualDetCol==4)
        {
            #ifndef MUNJIZA_MINATO
            printf("\nO método munjiza-minato foi desabilitado durante a compilação");
            printf("\nSe deseja habilitá-lo, defina MUNJIZA_MINATO em Munjiza.h\n");
            abort();
            #endif
            #ifdef MUNJIZA_MINATO
            cout << "Detecção (Contato) Munjiza-Minato para Contato (Sequencial)\n";
            DetContatoEsferas = new Munjiza  ( min_rad,max_rad, pModel->BoxMin, pModel->BoxMax,&pModel->SPHList,contato);
            #endif
        }
        else
        {
            Imprime("Detecção (Contato)","Munjiza-NBS","Sequencial");
            DetContatoEsferas = new Munjiza_nbs  ( max_rad, pModel,contato);
        }
    }
    else
    {
        if (qualDetCol==1)
        {
            Imprime("Detecção (Contato)","Screening","Paralelo");
            //cout << "Detecção (Contato):\t\t"<< VERMELHO <<"Screening\t"<< VERDE <<"Paralelo" << BRANCO<<"\n";
            DetContatoEsferas = new ScreeningParalelo  ( max_rad, pModel,contato);
        }
        else if (qualDetCol==4)
        {
            printf ("\nErro! Não há Munjiza-Minato em Paralelo!\n");
            printf ("Rodando a versão original...\n");

            #ifndef MUNJIZA_MINATO
            printf("\nO método munjiza-minato foi desabilitado durante a compilação");
            printf("\nSe deseja habilitá-lo, defina MUNJIZA_MINATO em Munjiza.h e recompile o programa\n");
            abort();
            #endif
            #ifdef MUNJIZA_MINATO
            //cout << "Detecção: Munjiza-Minato (Sequencial)\n";

            Imprime("Detecção (Contato)","Munjiza-Minato","Sequencial");
            DetContatoEsferas = new Munjiza  ( min_rad,max_rad, pModel->BoxMin, pModel->BoxMax,&pModel->SPHList,contato);
            #endif
        }
        else if (qualDetCol==2)
        {
            Imprime("Detecção (Contato)","Munjiza-NBS","Paralelo");
            DetContatoEsferas = new Munjiza_nbs_par  ( max_rad, pModel,contato);
        }
        #if (_MSC_VER >= 1700) || (__GNUC__ > 3)
        else
        {
            Imprime("Detecção (Contato)","Screening","Paralelo Condensado");
            DetContatoEsferas = new ScreeningCollapse  ( max_rad, pModel,contato);
        }
        #endif
    }
}

/*
Guarda no ponteiro *fronteira qual o algoritmo
a usar para o etapa de colisão com a fronteira
*/
void MED::IniciaFronteira()
{
    //Se for rígido, Instancio os modelos rígidos
    if (pModel->Boundary == MODEL::RIGID_BDR)
    {
        if (tudoSeq||frontSeq)
        {
            Imprime("Fronteira Rígida","Sequencial");
            fronteira = new FronteiraRigida(pModel);
        }
        else
        {
            Imprime("Fronteira Rígida","Paralelo");
            fronteira = new FronteiraRigidaParalelo(pModel);
        }
    }
    else if (pModel->Boundary == MODEL::DESTRUCTIVE_BDR)//Destrutível
    {
        if (tudoSeq||frontSeq)
        {
            Imprime("Fronteira Destrutível","Sequencial");
            fronteira = new FronteiraDestrutivel(pModel);
        }
        else
        {
            Imprime("Fronteira Destrutível","Paralelo");
            fronteira = new FronteiraDestrutivelParalelo(pModel);
        }
    }
    else if (pModel->Boundary == MODEL::ABSORTIVE_PARTIAL_BDR)
    {
        if (tudoSeq||frontSeq)
        {
            Imprime("Fronteira Absortiva (Parcial)","Sequencial");
            fronteira = new FronteiraAbsortivaParcial(pModel);
        }
        else
        {
            Imprime("Fronteira Absortiva (Parcial)","Paralelo");
            fronteira = new FronteiraAbsortivaParcialParalelo(pModel);
        }
    }
    else
    {
        if (tudoSeq||frontSeq)
        {
            Imprime("Fronteira Absortiva (Total)","Sequencial");
            fronteira = new FronteiraAbsortiva(pModel);
        }
        else
        {
            Imprime("Fronteira Absortiva (Total)","Paralelo");
            fronteira = new FronteiraAbsortivaParalelo(pModel);
        }
    }
}
void MED::EscolhaCampo()
{
    if (tudoSeq&&(qualDetCampo==3))
    {
        Imprime("Detecção (Campo de Força)","Screening","Sequencial");
        calculoCampoForca = new Screening ( pModel->FF_Range, pModel,campoForca);
    }
    else if (tudoSeq)
    {
        Imprime("Detecção (Campo de Força)","Munjiza-NBS","Sequencial");
        calculoCampoForca = new Munjiza_nbs ( pModel->FF_Range, pModel,campoForca);
    }
    #if (_MSC_VER >= 1700) || (__GNUC__ > 3)
    else if (qualDetCampo==2)
    {
        Imprime("Detecção (Campo de Força)","Screening","Paralelo Condensado");
        calculoCampoForca = new ScreeningCollapse ( pModel->FF_Range, pModel, campoForca);
    }
    #endif
    else if (qualDetCampo==3)
    {
        Imprime("Detecção (Campo de Força)","Screening","Paralelo");
        calculoCampoForca = new ScreeningParalelo  ( pModel->FF_Range, pModel, campoForca);
    }
    else if (qualDetCampo==4)
    {
        Imprime("Detecção (Campo de Força)","Munjiza-NBS","Paralelo");
        calculoCampoForca = new Munjiza_nbs_par  ( pModel->FF_Range, pModel, campoForca);
    }
}

void MED::IniciaRestricoes()
{
    if (tudoSeq||restrSeq)
    {
        Imprime("Cálculo de Restrições","Sequencial");
        restricoes = new Restricoes(pModel);
    }
    else
    {
        Imprime("Cálculo de Restrições","Paralelo");
        restricoes = new RestricoesParalelo(pModel);
    }
}

void MED::IniciaForcasIndependentes()
{
    if (tudoSeq||indepSeq)
    {
        Imprime("Forças Independentes","Sequencial");
        forcasIndependentes = new ForcasIndependentes (pModel);
    }
    else
    {
        Imprime("Forças Independentes","Paralelo");
        forcasIndependentes = new ForcasIndependentesParalelo (pModel);
    }
}
